<template>
  <div 
    ref="containerRef" 
    class="virtual-scroll-container"
    :style="{ height: containerHeight + 'px' }"
    @scroll="handleScroll"
  >
    <!-- 虚拟占位符 - 上方 -->
    <div 
      class="virtual-spacer"
      :style="{ height: offsetY + 'px' }"
    ></div>
    
    <!-- 可见项目渲染区域 -->
    <div class="virtual-items">
      <div
        v-for="(item, index) in visibleItems"
        :key="getItemKey(item, startIndex + index)"
        class="virtual-item"
        :style="{ height: itemHeight + 'px' }"
      >
        <slot 
          :item="item" 
          :index="startIndex + index"
          :isVisible="true"
        >
          {{ item }}
        </slot>
      </div>
    </div>
    
    <!-- 虚拟占位符 - 下方 -->
    <div 
      class="virtual-spacer"
      :style="{ height: (totalHeight - offsetY - visibleHeight) + 'px' }"
    ></div>
    
    <!-- 加载更多指示器 -->
    <div 
      v-if="hasMore && isNearBottom"
      class="load-more-indicator"
    >
      <slot name="loading">
        <div class="loading-content">
          <el-icon class="loading-icon"><Loading /></el-icon>
          <span>加载更多...</span>
        </div>
      </slot>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
import { Loading } from '@element-plus/icons-vue'

const props = defineProps({
  // 数据列表
  items: {
    type: Array,
    required: true
  },
  // 每项高度
  itemHeight: {
    type: Number,
    default: 50
  },
  // 容器高度
  containerHeight: {
    type: Number,
    default: 400
  },
  // 缓冲区大小（额外渲染的项目数）
  bufferSize: {
    type: Number,
    default: 5
  },
  // 获取项目唯一键的函数
  keyField: {
    type: [String, Function],
    default: 'id'
  },
  // 是否有更多数据
  hasMore: {
    type: Boolean,
    default: false
  },
  // 触发加载更多的距离
  loadMoreThreshold: {
    type: Number,
    default: 100
  }
})

const emit = defineEmits(['loadMore', 'scroll'])

const containerRef = ref(null)
const scrollTop = ref(0)

// 计算总高度
const totalHeight = computed(() => {
  return props.items.length * props.itemHeight
})

// 计算可见区域高度
const visibleHeight = computed(() => {
  return Math.min(props.containerHeight, totalHeight.value)
})

// 计算可见项目数量
const visibleCount = computed(() => {
  return Math.ceil(props.containerHeight / props.itemHeight) + props.bufferSize * 2
})

// 计算开始索引
const startIndex = computed(() => {
  const index = Math.floor(scrollTop.value / props.itemHeight) - props.bufferSize
  return Math.max(0, index)
})

// 计算结束索引
const endIndex = computed(() => {
  const index = startIndex.value + visibleCount.value
  return Math.min(props.items.length, index)
})

// 计算可见项目
const visibleItems = computed(() => {
  return props.items.slice(startIndex.value, endIndex.value)
})

// 计算偏移量
const offsetY = computed(() => {
  return startIndex.value * props.itemHeight
})

// 检查是否接近底部
const isNearBottom = computed(() => {
  const scrollBottom = scrollTop.value + props.containerHeight
  const threshold = totalHeight.value - props.loadMoreThreshold
  return scrollBottom >= threshold
})

// 获取项目键值
const getItemKey = (item, index) => {
  if (typeof props.keyField === 'function') {
    return props.keyField(item, index)
  }
  return item[props.keyField] || index
}

// 处理滚动事件
const handleScroll = (event) => {
  scrollTop.value = event.target.scrollTop
  
  emit('scroll', {
    scrollTop: scrollTop.value,
    startIndex: startIndex.value,
    endIndex: endIndex.value
  })
  
  // 检查是否需要加载更多
  if (props.hasMore && isNearBottom.value) {
    emit('loadMore')
  }
}

// 滚动到指定索引
const scrollToIndex = (index) => {
  if (!containerRef.value) return
  
  const targetScrollTop = index * props.itemHeight
  containerRef.value.scrollTop = targetScrollTop
}

// 滚动到顶部
const scrollToTop = () => {
  scrollToIndex(0)
}

// 滚动到底部
const scrollToBottom = () => {
  scrollToIndex(props.items.length - 1)
}

// 获取当前滚动信息
const getScrollInfo = () => {
  return {
    scrollTop: scrollTop.value,
    startIndex: startIndex.value,
    endIndex: endIndex.value,
    visibleCount: visibleCount.value,
    totalHeight: totalHeight.value
  }
}

// 监听数据变化，重置滚动位置
watch(() => props.items.length, (newLength, oldLength) => {
  // 如果数据长度发生变化，可能需要调整滚动位置
  if (newLength < oldLength) {
    // 数据减少时，确保滚动位置不超出范围
    const maxScrollTop = Math.max(0, totalHeight.value - props.containerHeight)
    if (scrollTop.value > maxScrollTop) {
      nextTick(() => {
        if (containerRef.value) {
          containerRef.value.scrollTop = maxScrollTop
        }
      })
    }
  }
})

// 暴露方法给父组件
defineExpose({
  scrollToIndex,
  scrollToTop,
  scrollToBottom,
  getScrollInfo
})

// 性能优化：使用 ResizeObserver 监听容器大小变化
let resizeObserver = null

onMounted(() => {
  if (containerRef.value && window.ResizeObserver) {
    resizeObserver = new ResizeObserver((entries) => {
      // 容器大小变化时重新计算
      // 这里可以添加额外的逻辑
    })
    resizeObserver.observe(containerRef.value)
  }
})

onUnmounted(() => {
  if (resizeObserver) {
    resizeObserver.disconnect()
  }
})
</script>

<style scoped>
.virtual-scroll-container {
  overflow-y: auto;
  overflow-x: hidden;
  position: relative;
}

.virtual-spacer {
  width: 100%;
  pointer-events: none;
}

.virtual-items {
  position: relative;
}

.virtual-item {
  width: 100%;
  box-sizing: border-box;
}

.load-more-indicator {
  padding: 20px;
  text-align: center;
  border-top: 1px solid #ebeef5;
}

.loading-content {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  color: #909399;
  font-size: 14px;
}

.loading-icon {
  animation: rotate 1s linear infinite;
}

@keyframes rotate {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

/* 滚动条样式优化 */
.virtual-scroll-container::-webkit-scrollbar {
  width: 6px;
}

.virtual-scroll-container::-webkit-scrollbar-track {
  background: #f1f1f1;
  border-radius: 3px;
}

.virtual-scroll-container::-webkit-scrollbar-thumb {
  background: #c1c1c1;
  border-radius: 3px;
}

.virtual-scroll-container::-webkit-scrollbar-thumb:hover {
  background: #a8a8a8;
}

/* 响应式优化 */
@media (max-width: 768px) {
  .load-more-indicator {
    padding: 15px;
  }
  
  .loading-content {
    font-size: 13px;
  }
}
</style>